using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.RuleEngine;
using BizTalk.ESB.Extensions.Helpers;
using System.Xml;
using System.Reflection;
using System.Linq;
using System.Linq.Expressions;

namespace BizTalk.ESB.Extensions.Validation.Helpers
{
    [Serializable]
    public static class Validator
    {
        public static ValidationResults ValidateWithBRE(XmlDocument XmlMessage, String XmlDocType
            , String PolicyName, string PolicyVersion, String ProcessName
            , out Boolean HasFailures, Boolean DebugFlag, String DebugTraceFileFullPath = "")
        {
            //initialize for return
            ValidationResults results = new ValidationResults();
            results.ProcessName = ProcessName;

            // validate message as single entityXmlDocument entityDoc = new XmlDocument();
            TypedXmlDocument typedDoc = new TypedXmlDocument(XmlDocType, XmlMessage);
            object[] facts = loadPolicyFacts(typedDoc, results);

            try
            {
                Policy policy;
                if (!string.IsNullOrEmpty(PolicyVersion))
                {
                    var VersionArray = PolicyVersion.Split(".".ToCharArray());
                    if (VersionArray != null)
                    {
                        var PolicyMajorVersion = Convert.ToInt16(VersionArray[0], System.Globalization.NumberFormatInfo.CurrentInfo);
                        var PolicyMinorVersion = Convert.ToInt16(VersionArray[1], System.Globalization.NumberFormatInfo.CurrentInfo);
                        policy = new Policy(PolicyName, PolicyMajorVersion, PolicyMinorVersion);
                    }
                    else
                    {
                        System.Diagnostics.Trace.WriteLine("Validator - Invalid Policy Version.  Ignoring provided value and using deployed policy with highest version number.");
                        policy = new Policy(PolicyName);
                    }
                }
                else
                {
                    policy = new Policy(PolicyName);
                }
                if (DebugFlag && String.IsNullOrEmpty(DebugTraceFileFullPath) == false)
                {
                    DebugTrackingInterceptor dti = new DebugTrackingInterceptor(DebugTraceFileFullPath);
                    policy.Execute(facts, dti);
                }
                else
                {
                    policy.Execute(facts);
                }
                policy.Dispose();
            }
            catch (Exception ex)
            {
                if (DebugFlag)
                {
                    System.Diagnostics.Debug.WriteLineIf(DebugFlag,
                        DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                            , Guid.NewGuid().ToString()
                            , "EXCEPTION EXECUTING BRE POLICY: " + PolicyName
                            , System.Diagnostics.EventLogEntryType.Error)
                        );
                }
                if (ex.InnerException != null)
                {
                    if (String.IsNullOrEmpty(ex.InnerException.Message) == false)
                    {
                        results.AddValidationItem(String.Empty, String.Empty, XmlMessage.DocumentElement.LocalName, "Error", String.Empty
                            , ex.Message + " " + ex.InnerException.Message, "99", "Structure");
                    }
                    else
                    {
                        results.AddValidationItem(String.Empty, String.Empty, XmlMessage.DocumentElement.LocalName, "Error", String.Empty
                            , ex.Message, "99", "Structure");
                    }
                }
                else
                {
                    results.AddValidationItem(String.Empty, String.Empty, XmlMessage.DocumentElement.LocalName, "Error", String.Empty
                        , ex.Message, "99", "Structure");
                }
            }

            Int32 InvalidCount = 0;
            if (results.Elements != null)
            {
                foreach (ValidationResultsElement result in results.Elements)
                {
                    if (result.Action.ToUpper() == "INVALID" || result.Action.ToUpper() == "ERROR")
                    {
                        InvalidCount = InvalidCount + 1;
                    }
                }
                HasFailures = (InvalidCount > 0);
                if (HasFailures)
                {
                    results.Result = ValidationResultsResult.Invalid;
                }
            }
            else
            {
                HasFailures = false;
            }

            if (DebugFlag)
            {
                if (results.Elements == null)
                {
                    System.Diagnostics.Debug.WriteLineIf(DebugFlag,
                        DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                            , Guid.NewGuid().ToString()
                            , "Validation Element Count = 0"
                            , System.Diagnostics.EventLogEntryType.Information)
                        );
                }
                else
                {
                    System.Diagnostics.Debug.WriteLineIf(DebugFlag,
                        DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                            , Guid.NewGuid().ToString()
                            , "Validation Element Count = " + results.Elements.Length.ToString()
                            , System.Diagnostics.EventLogEntryType.Information)
                        );
                }
                System.Diagnostics.Debug.WriteLineIf(DebugFlag,
                    DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                        , Guid.NewGuid().ToString()
                        , "HasFailures variable = " + HasFailures.ToString()
                        , System.Diagnostics.EventLogEntryType.Information)
                    );
                System.Diagnostics.Debug.WriteLineIf(DebugFlag,
                    DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                        , Guid.NewGuid().ToString()
                        , "Results with Action = 'Invalid' or 'Error' = " + InvalidCount.ToString()
                        , System.Diagnostics.EventLogEntryType.Information)
                    );
            }

            return results;
        }

        public static XmlDocument ValidateWithBRE2(XmlDocument XmlMessage, String XmlDocType
            , String PolicyName, String ProcessName, out Boolean HasFailures
            , Boolean DebugFlag, String DebugTraceFileFullPath = "")
        {
            //initialize for return
            ValidationResults results = new ValidationResults();
            results.ProcessName = ProcessName;

            // validate message as single entityXmlDocument entityDoc = new XmlDocument();
            TypedXmlDocument typedDoc = new TypedXmlDocument(XmlDocType, XmlMessage);
            object[] facts = loadPolicyFacts(typedDoc, results);

            try
            {
                Policy policy = new Policy(PolicyName);
                if (DebugFlag && String.IsNullOrEmpty(DebugTraceFileFullPath) == false)
                {
                    DebugTrackingInterceptor dti = new DebugTrackingInterceptor(DebugTraceFileFullPath);
                    policy.Execute(facts, dti);
                }
                else
                {
                    policy.Execute(facts);
                }
                policy.Dispose();
            }
            catch (Exception ex)
            {
                if (DebugFlag)
                {
                    System.Diagnostics.Debug.WriteLineIf(DebugFlag,
                        DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                            , Guid.NewGuid().ToString()
                            , "EXCEPTION EXECUTING BRE POLICY: " + PolicyName
                            , System.Diagnostics.EventLogEntryType.Error)
                        );
                }

                if (ex.InnerException != null)
                {
                    if (String.IsNullOrEmpty(ex.InnerException.Message) == false)
                    {
                        results.AddValidationItem(String.Empty, String.Empty, XmlMessage.DocumentElement.LocalName, "Error", String.Empty
                            , ex.Message + " " + ex.InnerException.Message, "99", "Structure");
                    }
                    else
                    {
                        results.AddValidationItem(String.Empty, String.Empty, XmlMessage.DocumentElement.LocalName, "Error", String.Empty
                            , ex.Message, "99", "Structure");
                    }
                }
                else
                {
                    results.AddValidationItem(String.Empty, String.Empty, XmlMessage.DocumentElement.LocalName, "Error", String.Empty
                        , ex.Message, "99", "Structure");
                }
            }

            Int32 InvalidCount = 0;
            if (results.Elements != null)
            {
                foreach (ValidationResultsElement result in results.Elements)
                {
                    if (result.Action.ToUpper() == "INVALID" || result.Action.ToUpper() == "ERROR")
                    {
                        InvalidCount = InvalidCount + 1;
                    }
                }
                HasFailures = (InvalidCount > 0);
                if (HasFailures)
                {
                    results.Result = ValidationResultsResult.Invalid;
                }
            }
            else
            {
                HasFailures = false;
            }

            if (DebugFlag)
            {
                if (results.Elements == null)
                {
                    System.Diagnostics.Debug.WriteLineIf(DebugFlag,
                        DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                            , Guid.NewGuid().ToString()
                            , "Validation Element Count = 0"
                            , System.Diagnostics.EventLogEntryType.Information)
                        );
                }
                else
                {
                    System.Diagnostics.Debug.WriteLineIf(DebugFlag,
                        DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                            , Guid.NewGuid().ToString()
                            , "Validation Element Count = " + results.Elements.Length.ToString()
                            , System.Diagnostics.EventLogEntryType.Information)
                        );
                }
                System.Diagnostics.Debug.WriteLineIf(DebugFlag,
                    DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                        , Guid.NewGuid().ToString()
                        , "HasFailures variable = " + HasFailures.ToString()
                        , System.Diagnostics.EventLogEntryType.Information)
                    );
                System.Diagnostics.Debug.WriteLineIf(DebugFlag,
                    DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                        , Guid.NewGuid().ToString()
                        , "Results with Action = 'Invalid' or 'Error' = " + InvalidCount.ToString()
                        , System.Diagnostics.EventLogEntryType.Information)
                    );
            }

            return SerializationHelpers.SerializeAnObject(results);
        }

        public static System.Xml.XmlDocument ValidateWithBRE(System.Xml.XmlDocument XmlMessage
            , System.Collections.Specialized.HybridDictionary dictionary
            , String ProcessName
            , out Boolean HasFailures)
        {
            //initialize for return
            ValidationResults results = new ValidationResults();
            results.ProcessName = ProcessName;

            string valEntity = string.Empty;

            //get entities to validate
            if (dictionary.Contains("ValidationTypes"))
            {
                string valTypes = dictionary["ValidationTypes"].ToString();
                string[] valTypesArray = StringTools.SplitString(valTypes, ",");

                if (valTypesArray != null)
                {
                    string valTypeKey = string.Empty;
                    string xpath = string.Empty;

                    foreach (string valType in valTypesArray)
                    {
                        valEntity = valType;
                        valTypeKey = valType + "XPath";

                        //tells us where to break apart the doc to get the entity
                        xpath = dictionary[valTypeKey].ToString();

                        //gets us all of the entity nodes in the request
                        XmlNodeList entityList = XmlMessage.SelectNodes(xpath);

                        if (entityList != null)
                        {
                            int i = 0;
                            foreach (XmlNode entity in entityList)
                            {
                                i = i + 1;
                                XmlDocument entityDoc = new XmlDocument();
                                entityDoc.LoadXml(entity.OuterXml);

                                String documentType = dictionary[valType + "DocType"].ToString();
                                TypedXmlDocument typedDoc = new TypedXmlDocument(documentType, entityDoc);
                                object[] facts = loadPolicyFacts(typedDoc, results);

                                try
                                {
                                    DebugTrackingInterceptor dti = new DebugTrackingInterceptor(@"C:\DanRulesTracking.txt");

                                    Policy policy = new Policy(valType + "Validation");
                                    policy.Execute(facts, dti);
                                    policy.Dispose();
                                }
                                catch (Exception ex)
                                {
                                    if (ex.InnerException != null)
                                    {
                                        if (String.IsNullOrEmpty(ex.InnerException.Message) == false)
                                        {
                                            results.AddValidationItem(String.Empty, String.Empty, valEntity, "Error", String.Empty
                                                , ex.Message + " " + ex.InnerException.Message, "99", "Structure");
                                        }
                                        else
                                        {
                                            results.AddValidationItem(String.Empty, String.Empty, valEntity, "Error", String.Empty
                                                , ex.Message, "99", "Structure");
                                        }
                                    }
                                    else
                                    {
                                        results.AddValidationItem(String.Empty, String.Empty, valEntity, "Error", String.Empty
                                            , ex.Message, "99", "Structure");
                                    }
                                }
                            }
                        }
                    }
                }
            }
            Int32 InvalidCount = 0;
            if (results.Elements != null)
            {
                foreach (ValidationResultsElement result in results.Elements)
                {
                    if (result.Action.ToUpper() == "INVALID" || result.Action.ToUpper() == "ERROR")
                    {
                        InvalidCount = InvalidCount + 1;
                    }
                }
                HasFailures = (InvalidCount > 0);
            }
            else
            {
                HasFailures = false;
            }
            Boolean debugFlag = System.Convert.ToBoolean(dictionary["DebugFlag"]);
            if (debugFlag)
            {
                if (results.Elements == null)
                {
                    System.Diagnostics.Debug.WriteLineIf(debugFlag,
                        DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                            , Guid.NewGuid().ToString()
                            , "Validation Element Count = 0"
                            , System.Diagnostics.EventLogEntryType.Information)
                        );
                }
                else
                {
                    System.Diagnostics.Debug.WriteLineIf(debugFlag,
                        DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                            , Guid.NewGuid().ToString()
                            , "Validation Element Count = " + results.Elements.Length.ToString()
                            , System.Diagnostics.EventLogEntryType.Information)
                        );
                }
                System.Diagnostics.Debug.WriteLineIf(debugFlag,
                    DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                        , Guid.NewGuid().ToString()
                        , "HasFailures variable = " + HasFailures.ToString()
                        , System.Diagnostics.EventLogEntryType.Information)
                    );
                System.Diagnostics.Debug.WriteLineIf(debugFlag,
                    DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                        , Guid.NewGuid().ToString()
                        , "Results with Action = 'Invalid' or 'Error' = " + InvalidCount.ToString()
                        , System.Diagnostics.EventLogEntryType.Information)
                    );
            }

            return SerializationHelpers.SerializeAnObject(results);
        }

        public static XmlDocument ValidateWithBRE(XmlDocument MessageToValidate, string ValidationPolicyName
            , string ProcessName, string DocumentType, bool DebugFlag, out bool HasErrors, string DebugTraceFileFullPath = "")
        {
            //initialize for return
            HasErrors = false;
            var results = new ValidationResults();
            results.ProcessName = ProcessName;
            var validationResults = new XmlDocument();

            System.Diagnostics.Debug.WriteLineIf(DebugFlag,
                DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                    , Guid.NewGuid().ToString()
                    , "Xml strong document type = " + DocumentType
                    , System.Diagnostics.EventLogEntryType.Information)
                );

            System.Diagnostics.Debug.WriteLineIf(DebugFlag,
                DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                    , Guid.NewGuid().ToString()
                    , "BRE Trace File = " + DebugTraceFileFullPath
                    , System.Diagnostics.EventLogEntryType.Information)
                );

            var typedDoc = new TypedXmlDocument(DocumentType, MessageToValidate);
            object[] facts = loadPolicyFacts(typedDoc, results);

            System.Diagnostics.Debug.WriteLineIf(DebugFlag,
                DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                    , Guid.NewGuid().ToString()
                    , "BRE Facts collection Count = " + facts.Length.ToString()
                    , System.Diagnostics.EventLogEntryType.Information)
                );

            try
            {
                var policy = new Policy(ValidationPolicyName);
                if (string.IsNullOrEmpty(DebugTraceFileFullPath))
                {
                    policy.Execute(facts);
                }
                else
                {
                    var dti = new DebugTrackingInterceptor(DebugTraceFileFullPath);
                    policy.Execute(facts, dti);
                }
                policy.Dispose();
            }
            catch (Exception ex)
            {
                if (DebugFlag)
                {
                    System.Diagnostics.Debug.WriteLineIf(DebugFlag,
                        DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                            , Guid.NewGuid().ToString()
                            , "EXCEPTION EXECUTING BRE POLICY: " + ValidationPolicyName
                            , System.Diagnostics.EventLogEntryType.Error)
                        );
                }
                if (ex.InnerException != null)
                {
                    if (String.IsNullOrEmpty(ex.InnerException.Message) == false)
                    {
                        results.AddValidationItem(String.Empty, String.Empty, DocumentType, "Error", String.Empty
                            , ex.Message + " " + ex.InnerException.Message, "99", "Structure");
                    }
                    else
                    {
                        results.AddValidationItem(String.Empty, String.Empty, DocumentType, "Error", String.Empty
                            , ex.Message, "99", "Structure");
                    }
                }
                else
                {
                    results.AddValidationItem(String.Empty, String.Empty, DocumentType, "Error", String.Empty
                        , ex.Message, "99", "Structure");
                }
            }

            int InvalidCount = 0;
            if (results.Elements != null)
            {
                foreach (ValidationResultsElement result in results.Elements)
                {
                    if (result.Action.ToUpper() == "INVALID" || result.Action.ToUpper() == "ERROR")
                    {
                        InvalidCount = InvalidCount + 1;
                    }
                }
                HasErrors = (InvalidCount > 0);
                if (HasErrors)
                {
                    results.Result = ValidationResultsResult.Invalid;
                }
            }
            else
            {
                HasErrors = false;
            }

            if (DebugFlag)
            {
                if (results.Elements == null)
                {
                    System.Diagnostics.Debug.WriteLineIf(DebugFlag,
                        DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                            , Guid.NewGuid().ToString()
                            , "Validation Element Count = 0"
                            , System.Diagnostics.EventLogEntryType.Information)
                        );
                }
                else
                {
                    System.Diagnostics.Debug.WriteLineIf(DebugFlag,
                        DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                            , Guid.NewGuid().ToString()
                            , "Validation Element Count = " + results.Elements.Length.ToString()
                            , System.Diagnostics.EventLogEntryType.Information)
                        );
                }
                System.Diagnostics.Debug.WriteLineIf(DebugFlag,
                    DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                        , Guid.NewGuid().ToString()
                        , "HasFailures variable = " + HasErrors.ToString()
                        , System.Diagnostics.EventLogEntryType.Information)
                    );
                System.Diagnostics.Debug.WriteLineIf(DebugFlag,
                    DebugMessageFactory.GenerateTraceMessage("ValidationHelper"
                        , Guid.NewGuid().ToString()
                        , "Results with Action = 'Invalid' or 'Error' = " + InvalidCount.ToString()
                        , System.Diagnostics.EventLogEntryType.Information)
                    );
            }

            return validationResults;
        }


        //sets up facts array with common objects for assertion in rules engine
        private static object[] loadPolicyFacts(TypedXmlDocument typedDoc, ValidationResults results)
        {
            StringFunctions stringFunctions = new StringFunctions();

            object[] facts = new object[3];

            facts[0] = results;
            facts[1] = typedDoc;
            facts[2] = stringFunctions;

            return facts;
        }
    }
}
